{$CLEO .cs}
{$USE cleo+}
nop
script_name 'VHSPFIX'
wait 0
wait 0

if
    load_dynamic_library 31@ "CLEO+.cleo"
jf @ILoveMyGirlfriendJessica
if
    get_dynamic_library_procedure 30@ "GetCleoPlusVersion" library 31@
then
    call_function_return 30@ num_params 0 pop 0 29@
end
free_dynamic_library 31@
if
    29@ >= 0x1020000
jf @ILoveMyGirlfriendJessica


int i1, i2, i3
int iCounter, iCoord, iFloatCoordPtr, iVarOffset
float fCoord, fX, fY, fZ
float f1, f2, f3, f4, f5, f6


// Disable height check in CCarGenerator::CheckIfWithinRangeOfAnyPlayers
read_memory i1 0x6F2FC4 size 4 virtual_protect 0
if
    i1 == 0x011A840F
then
    make_nop 0x6F2FC4 size 6
end

// Patch CCarGenerator::CheckForBlockage - for helicopters at Flight School
// Checks if function isn't already hacked and multiplies the search radius with an extra multiplier
for i1 = 0x6F32D2 to 0x6F32E0 step 0x02
    if
        i1 == 0x6F32E0
    then
        read_memory i1 0x6F32FB size 4 virtual_protect 0
        read_memory i2 0x6F3300 size 4 virtual_protect 0
        if and
            i1 == 0x518BC88B
            i2 == 0x10245489
        jf break
        // Code Cave: fld [eax+24]
        write_memory 0x6F32D2 size 4 value 0xD82440D9 virtual_protect 0        
        // fmul dword ptr [0x6F32DC]
        write_memory 0x6F32D6 size 4 value 0x6F32DC0D virtual_protect 0        
        // ret
        write_memory 0x6F32DA size 2 value 0xC300 virtual_protect 0       
        // Extra float multiplier
        write_memory 0x6F32DC size 4 value 0.6 virtual_protect 0        
        // Call code cave from 0x6F32FB
        write_memory 0x6F32FB size 4 value 0xFFFFD2E8 virtual_protect 0
        write_memory 0x6F32FF size 1 value 0xFF virtual_protect 0       
        // Replace mov [esp+10], edx with fstp [esp+10]
        write_memory 0x6F3300 size 4 value 0x10245CD9 virtual_protect 0
        break
    end               
    read_memory i2 i1 size 2 virtual_protect 0
    if
        i2 == 0x9090
    jf break
end

{
VALIDATE_OFFSET(CCarGenerator, m_nModelId, 0x0);
VALIDATE_OFFSET(CCarGenerator, m_nColor1, 0x2);
VALIDATE_OFFSET(CCarGenerator, m_nColor2, 0x3);
VALIDATE_OFFSET(CCarGenerator, m_vecPosn, 0x4);
VALIDATE_OFFSET(CCarGenerator, m_nAngle, 0xA);
VALIDATE_OFFSET(CCarGenerator, m_nAlarm, 0xB);
VALIDATE_OFFSET(CCarGenerator, m_nDoorLock, 0xC);
VALIDATE_OFFSET(CCarGenerator, m_nFlags, 0xD);
VALIDATE_OFFSET(CCarGenerator, m_nMinDelay, 0xE);
VALIDATE_OFFSET(CCarGenerator, m_nMaxDelay, 0x10);
VALIDATE_OFFSET(CCarGenerator, m_nNextGenTime, 0x14);
VALIDATE_OFFSET(CCarGenerator, m_nVehicleHandle, 0x18);
VALIDATE_OFFSET(CCarGenerator, m_nGenerateCount, 0x1A);
VALIDATE_OFFSET(CCarGenerator, m_nIplId, 0x1C);
VALIDATE_OFFSET(CCarGenerator, m_bIsUsed, 0x1D);
VALIDATE_SIZE(CCarGenerator, 0x20);

Flags:
unsigned char bWaitUntilFarFromPlayer : 1;
unsigned char bHighPriority : 1;
unsigned char bActive : 1;
unsigned char bPlayerHasAlreadyOwnedCar : 1;
unsigned char bIgnorePopulationLimit : 1;
}


get_var_pointer iFloatCoordPtr = var fX

// Patch Car Generators created by main.scm. They are always in the array and stay in savegame

for i1 = 0 to 499
    iCounter += 1
    if
        iCounter >= 24
    then
        iCounter = 0
        wait 0
    end
    
    gosub @GetCarGenCoords
    
    if
        iVarOffset <> 0
    jf continue
    
    get_distance_between_coords_3d f1 fX fY fZ -1460.87 -1566.737 101.0579  // Sanchez at Whetstone Farm
    get_distance_between_coords_3d f2 fX fY fZ -1446.239 -1494.731 101.0514 // Walton at Whetstone Farm 
    get_distance_between_coords_3d f3 fX fY fZ -1439.643 -1576.823 101.0578 // Tractor at Whetstone Farm 
    get_distance_between_coords_3d f4 fX fY fZ -2048.856 -2521.276 31.125   // Sanchez at Angel Pine Trailer
     
    if or
        f1 < 2.0
        f2 < 2.0
        f3 < 2.0
        f4 < 2.0
    then
        gosub @PatchCarGenFlags
        continue
    end
      
    get_distance_between_coords_3d f1 fX fY fZ 435.2751 2527.523 16.371     // PCJ600 at Desert Airport
    get_distance_between_coords_3d f2 fX fY fZ 292.0 2542.0 16.0            // Hydra at Desert Airport
    get_distance_between_coords_3d f3 fX fY fZ 2092.0 2415.0 75.0           // Maverick at Emerald Isle      
    get_distance_between_coords_3d f4 fX fY fZ 1291.5 -787.5 95.4555        // Sparrow at Madd Dogg's Mansion
    get_distance_between_coords_3d f5 fX fY fZ 1305.176 -796.6955 83.9477   // Kart at Madd Dogg's Mansion
    
    if or
        f1 < 2.0
        f2 < 2.0
        f3 < 2.0
        f4 < 2.0
        f5 < 2.0
    then
        gosub @PatchCarGenFlags
        continue
    end
         
    get_distance_between_coords_3d f1 fX fY fZ 1174.467 1368.359 10.1203    // NRG500 at Bike School
    if
        f1 < 1.0
    then
        read_memory i3 i2 size 2 virtual_protect 0
        if
            i3 == 522
        then           
            write_struct_offset i2 offset 0x6 size 2 value 10968    // Change y to 1371.17
            continue
        end
    end     
    get_distance_between_coords_3d f1 fX fY fZ 1174.999 1366.479 10.1203    // FCR900 at Bike School
    if
        f1 < 1.0
    then
        read_memory i3 i2 size 2 virtual_protect 0
        if
            i3 == 521
        jf continue 
        write_struct_offset i2 offset 0x6 size 2 value 10943    // Change y to 1368.04
    end 
end


i1 = 0

// Patch Car Generators from streamed IPLs when leaving interiors. These aren't always in array and don't go into savegame.

while true
    wait 0
    get_area_visible i2
    if
        i2 <> 0
    then
        i1 = 0
        continue
    end
    if
        i1 == 0
    jf continue
    
    repeat
        wait 0
        get_char_coordinates $PLAYER_ACTOR fX fY fZ
    until fZ < 900.0
        
    for i1 = 0 to 499
        gosub @GetCarGenCoords       
        if
            iVarOffset <> 0
        jf continue
        get_distance_between_coords_3d f1 fX fY fZ -1436.589 -1482.949 100.761  // Another Tractor at Whetstone Farm
        get_distance_between_coords_3d f2 fX fY fZ -680.580 948.198 11.292      // Washington at Toreno Ranch
        get_distance_between_coords_3d f3 fX fY fZ -1055.069 1543.510 32.879    // Sanchez at Tierra Robada Safehouse
        get_distance_between_coords_3d f4 fX fY fZ 2822.399 2160.010 10.341     // Mountain Bike at Creek Safehouse       
        get_distance_between_coords_3d f5 fX fY fZ 878.883 -1658.800 12.719     // Vehicles at Verona Beach Safehouse
        get_distance_between_coords_3d f6 fX fY fZ 1248.739 -803.838 83.921     // Vehicles in front of Madd Dogg's Mansion garage            
        if and
            f1 > 2.0
            f2 > 2.0
            f3 > 2.0
            f4 > 2.0
            f5 > 2.0
            f6 > 2.0
        gosub_if_false @PatchCarGenFlags
    end
end


:GetCarGenCoords
iVarOffset = 0
call_function_return 0x479D60 num_params 1 pop 1 i1 store_result_to i2 // CCarGenerator* ptr
if
    i2 <> 0
return_if_false

read_memory i3 i2 size 2 virtual_protect 0  // Check model ID
if
    i3 > 0   
return_if_false

// Read and transform position
for i3 = 0x4 to 0x8 step 0x2
    read_struct_offset iCoord i2 offset i3 size 2
    cset_lvar_float_to_lvar_int fCoord = integer iCoord
    // For negative coordinates
    if 
        fCoord > 32767.0  // If value > 32767, it's negative in signed 16-bit
    then
        fCoord -= 65536.0 
    end
    fCoord *= 0.125
    write_struct_offset iFloatCoordPtr offset iVarOffset size 4 value fCoord
    iVarOffset += 0x4
end
return


:PatchCarGenFlags
read_struct_offset i3 i2 offset 0xD size 1
set_local_var_bit_const i3 bit 1
set_local_var_bit_const i3 bit 3
write_struct_offset i2 offset 0xD size 1 value i3
return


:ILoveMyGirlfriendJessica
TIMERA = 0

repeat
    wait 0
    print_formatted_now "Vehicle Spawn Fix: CLEO+ 1.2.0 or newer required. It's a plugin for CLEO. Please download and install it." time 200
    if and
        is_player_control_on $PLAYER_CHAR
        not fading
    jf @ILoveMyGirlfriendJessica
until TIMERA > 10000

terminate_this_custom_script